package com.lin.fengxiu.service;
import com.lin.fengxiu.core.LocalUser;
import com.lin.fengxiu.core.enumeration.OrderStatus;
import com.lin.fengxiu.core.money.IMoneyDiscount;
import com.lin.fengxiu.dto.OrderDTO;
import com.lin.fengxiu.dto.SkuInfoDTO;
import com.lin.fengxiu.exception.http.ForbiddenException;
import com.lin.fengxiu.exception.http.NotFoundException;
import com.lin.fengxiu.exception.http.ParameterException;
import com.lin.fengxiu.logic.OrderChecker;
import com.lin.fengxiu.model.*;
import com.lin.fengxiu.logic.CouponChecker;
import com.lin.fengxiu.repository.CouponRepository;
import com.lin.fengxiu.repository.OrderRepository;
import com.lin.fengxiu.repository.SkuRepository;
import com.lin.fengxiu.repository.UserCouponRepository;
import com.lin.fengxiu.util.CommonUtil;
import com.lin.fengxiu.util.OrderUtil;
import org.aspectj.weaver.ast.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName OrderService
 * @Description TODO
 * @Author wushaopei
 * @Date 2021/4/3 11:40
 * @Version 1.0
 */
@Service
public class OrderService {

    @Autowired
    private SkuService skuService;

    @Autowired
    private CouponRepository couponRepository;

    @Autowired
    private SkuRepository skuRepository;

    @Autowired
    private UserCouponRepository userCouponRepository;

    @Autowired
    private IMoneyDiscount iMoneyDiscount;

    @Autowired
    private OrderRepository orderRepository;

    // 单个 sku 的最大购买量
    @Value("${fengxiu.order.max-sku-limit}")
    private int maxSkuLimit;

    // 延迟支付时间控制
    @Value("${fengxiu.order.pay-time-limit}")
    private Integer payTimeLimit;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * @Description TODO 下单逻辑
     * @param uid           用户id
     * @param orderDTO     订单DTO
     * @param orderChecker     订单校验
     */
    @Transactional
    public Long placeOrder(Long uid, OrderDTO orderDTO, OrderChecker orderChecker){
        // 获取订单号
        String orderNo = OrderUtil.makeOrderNo();
        // 订单的支付过期时间
        TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"));
        Calendar now = Calendar.getInstance();
        Calendar now1 = (Calendar)now.clone();
        Date expiredTime = CommonUtil.addSomeSeconds(now, this.payTimeLimit).getTime();

        // 生成订单
        Order order = Order.builder()
                .orderNo(orderNo)  // 订单号，必须具有唯一性
                .totalPrice(orderDTO.getTotalPrice())
                .finalTotalPrice(orderDTO.getFinalTotalPrice())
                .userId(uid)
                .totalCount(orderChecker.getTotalCount().longValue())
                .snapImg(orderChecker.getLeaderImg())
                .snapTitle(orderChecker.getLeaderTitle())
                // 订单支付超时时间
                .expiredTime(expiredTime)
                .placedTime(now1.getTime())
                // 需要将对象序列化
//                .snapAddress(orderDTO.getAddress())
                // 待支付
                .status(OrderStatus.UNPAID.value())
                .build();

        order.setSnapAddress(orderDTO.getAddress());
        order.setSnapItems(orderChecker.getOrderSkuList());
//        order.setCreateTime(now1.getTime());
        // 将订单写入DB
        this.orderRepository.save(order);
        // reduceStock 减库存
        this.reduceStock(orderChecker);
        // 核销优惠券
        if(orderDTO.getCouponId() != null){
            this.writeOffCoupon(orderDTO.getCouponId(),order.getId(),uid);
        }
        // 加入到延迟消息队列


        // 下单成功，将订单id返回
        return order.getId();
    }

    /**
     * @Description TODO 订单、优惠券信息写入redis
     * @param oid
     * @param uid
     * @param couponId
     */
    private void sendToRedis(Long oid, Long uid, Long couponId){

        // 设置的键由 oid 、uid、couponId组成
        String key = oid.toString()+","+uid.toString()+","+couponId.toString();

        // 该逻辑不抛出异常
        try {
            stringRedisTemplate.opsForValue().set(key, "1", this.payTimeLimit, TimeUnit.SECONDS);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * @Description TODO 分页查询未支付订单
     * @param page
     * @param size
     */
    public Page<Order> getUnpaid(Integer page, Integer size){
        Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending());
        Long uid = LocalUser.getUser().getId();

        Date now = new Date();
        Page<Order> pageOrder = this.orderRepository.findByExpiredTimeGreaterThanAndStatusAndUserId(now,OrderStatus.UNPAID.value(),uid,pageable);
        return pageOrder;
    }

    /**
     * @Description TODO 根据传入状态分页查询符合的订单
     * @param page
     * @param size
     */
    public Page<Order> getByStatus(Integer status, Integer page, Integer size){
        Pageable pageable = PageRequest.of(page, size, Sort.by("createTime").descending());
        Long uid = LocalUser.getUser().getId();

        if( status == OrderStatus.All.value()){
            return this.orderRepository.findByUserId(uid, pageable);
        }
        return this.orderRepository.findByUserIdAndStatus(uid,status,pageable);
    }

    /**
     * @Description TODO 查询订单详情
     * @param oid
     * @return
     */
    public Optional<Order> getOrderDetail(Long oid){
        Long uid = LocalUser.getUser().getId();
        return this.orderRepository.findFirstByUserIdAndId(uid,oid);
    }


    /**
     * @Description TODO 统一下单成功，将微信服务器订单id赋值给order模型的prepay_id
     * @param orderId
     * @param prePayId
     */
    public void updateOrderPrepayId(Long orderId, String prePayId){
        Optional<Order> order = this.orderRepository.findById(orderId);
        order.ifPresent(o ->{
            o.setPrepayId(prePayId);
            this.orderRepository.save(o);
        });

        order.orElseThrow(()->new ParameterException(10007));
    }

    /**
            * @Description TODO 核销优惠券
     * @param couponId
     * @param oid
     * @param uid
     */
    private void writeOffCoupon(Long couponId, Long oid, Long uid){
        int result = this.userCouponRepository.writeOff(couponId,oid,uid);
        if( result != 1){
            throw new ForbiddenException(40012);
        }
    }

    /***
     * @Description TODO 减库存
     * @param orderChecker
     */
    private void reduceStock(OrderChecker orderChecker){
        List<OrderSku> orderSkuList = orderChecker.getOrderSkuList();
        // for 循环减库存
        for (OrderSku orderSku : orderSkuList){
            Long sid = orderSku.getId();
            Long quantity = orderSku.getCount().longValue();
            int result = this.skuRepository.reduceStock(sid,quantity);
            // 判断减库存是否成功
            if( result != 1 ){
                throw new ParameterException(50003);
            }
        }
    }

    /**
     * @Description TODO 订单查询
     * @param uid
     * @param orderDTO
     */
    public OrderChecker isOK(Long uid, OrderDTO orderDTO){

        // 1.若FinalTotalPrice<=0则说明订单存在问题，抛出异常.
        if(orderDTO.getFinalTotalPrice().compareTo(new BigDecimal("0"))<=0){
            throw new ParameterException(50001);
        }

        // 2.查询订单的所有skuId，从而获取sku信息；该sku信息用于计算totalPrice
        // 获取所有的skuId
        List<Long> skuIdList = orderDTO.getSkuInfoList()
                .stream()
                .map(SkuInfoDTO::getId)
                .collect(Collectors.toList());
        // 根据skuIds查询订单内的skuList
        List<Sku> skuList = this.skuService.getSkuListByIds(skuIdList);

        // 3.订单若使用优惠券，则校验CouponChecker
        Long couponId = orderDTO.getCouponId();
        CouponChecker couponChecker = null;
        if(couponId != null){
            // 根据couponId 查询 Coupon
            Coupon coupon = this.couponRepository.findById(couponId)
                    .orElseThrow(()->new NotFoundException(40004));
            // 根据 uid 和 couponId 查询的UserCoupon
            UserCoupon userCoupon = this.userCouponRepository.findFirstByUserIdAndCouponIdAndStatus(uid,couponId,1)
                    .orElseThrow(()->new NotFoundException(50006));
            couponChecker = new CouponChecker(coupon,iMoneyDiscount);
        }
        // 订单支付金额校验,如果沒有使用優惠券，totalPrice和finalTotalPrice必須一直
        if(couponChecker == null && orderDTO.getFinalTotalPrice().compareTo(orderDTO.getTotalPrice()) != 0){
            throw new ParameterException(50005);
        }
        // 获取 OrderChecker 实例
        OrderChecker orderChecker = new OrderChecker(orderDTO,skuList,couponChecker,this.maxSkuLimit);
        // 调用 OrderChecker校验逻辑
        orderChecker.isOK();
        // 返回 OrderChecker 校验结果给前端
        return orderChecker;
    }
}
